package org.vacoor.xqq.core.mod;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vacoor.nothing.common.json.Jacksons;
import org.vacoor.nothing.common.util.Regexs;
import org.vacoor.xqq.core.EventBusHolder;
import org.vacoor.xqq.core.bean.*;
import org.vacoor.xqq.core.event.Avatar;
import org.vacoor.xqq.core.event.BuddyStatus;
import org.vacoor.xqq.core.event.Summary;
import org.vacoor.xqq.core.http.HttpRequestor;
import org.vacoor.xqq.core.http.Request;
import org.vacoor.xqq.core.http.RequestCallbackAdapter;
import org.vacoor.xqq.core.http.Response;
import org.vacoor.xqq.core.util.QQUtil;
import org.vacoor.xqq.reply.*;
import org.vacoor.xqq.reply.impl.result.*;

import javax.swing.*;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import static org.vacoor.xqq.core.Constants.*;

/**
 * @author: vacoor
 * <p/>
 * TODO 先加载缓存, 如果缓存不存在, 则使用默认头像, 然后去加载头像并存入缓存, 头像更新在每次状态改变
 */
public class BuddyManager {
    private static final Logger logger = LoggerFactory.getLogger(BuddyManager.class);

    private static final String GET_ACCOUNT = "http://s.web2.qq.com/api/get_friend_uin2";

    private ChatToken token;

    private Map<Integer, Category> categories = new LinkedHashMap<Integer, Category>();
    private Map<Long, Buddy> buddies = new LinkedHashMap<Long, Buddy>();
    private Map<Long, Discussion> discuGroups = new LinkedHashMap<Long, Discussion>();
    private Map<Long, Group> groups = new LinkedHashMap<Long, Group>();

    private OnlineStatusResolver onlineStatusResolver;
    private SummaryResolver summaryResolver;
    private BuddiesResolver buddiesResolver;
    private DiscuListResolver discuListResolver;
    private DiscuInfoResolver discuInfoResolver;
    private GroupListResolver groupListResolver;
    private GroupInfoResolver groupInfoResolver;

    public BuddyManager(ChatToken token) {
        this.token = token;
        this.onlineStatusResolver = new SimpleOnlineStatusResolver();
        this.summaryResolver = new SimpleSummaryResolver();
        this.buddiesResolver = new SimpleBuddiesResolver() {
            @Override
            protected Buddy createBuddy(long uin) {
                return new LazyBuddy(uin);
            }
        };

        this.discuListResolver = new SimpleDiscuListResolver();
        this.discuInfoResolver = new SimpleDiscuInfoResolver() {
            @Override
            protected Buddy createBuddy(long uin) {
                Buddy buddy = buddies.get(uin);
                return buddy != null ? buddy : new LazyBuddy(uin);
            }
        };

        this.groupListResolver = new SimpleGroupListResolver();
        this.groupInfoResolver = new SimpleGroupInfoResolver() {
            @Override
            protected Buddy createBuddy(long uin) {
                Buddy buddy = buddies.get(uin);
                return buddy != null ? buddy : new LazyBuddy(uin);
            }
        };
    }

    /**
     * 获取分组和基本好友信息
     *
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     * @throws IOException
     */
    public List<Category<Buddy>> getBuddies() throws ExecutionException, InterruptedException, IOException {
        String hash = QQUtil.hash(token.getUin(), token.getPtwebqq());

        String param = Jacksons.createObjectNode().put("vfwebqq", token.getVfwebqq()).put("hash", hash).toString();
        logger.debug("getBuddies : {}", param);

        Response resp = HttpRequestor.getInstance().send(
                new Request(U_GET_BUDDIES, Request.HttpMethod.POST)
                        .addHeader("Content-Type", "application/x-www-form-urlencoded")
                        .addHeader("Referer", H_REFERER)
                        .addHeader("Origin", "http://s.web2.qq.com")
                        .addHeader("Host", "s.web2.qq.com")
                        .addParameter("r", param),

                null
        ).get();

        String s = resp.getContent().asString();
        logger.debug("获取好友列表, 响应: {}", s);

        List<Category<Buddy>> cs = buddiesResolver.resolveReply(s);
        cs = null != cs ? cs : Collections.<Category<Buddy>>emptyList();
        Collections.sort(cs, new Sortable.SortableComparator());

        for (Category<Buddy> c : cs) {
            categories.put(c.getIndex(), c);
            for (Buddy buddy : c) {
                buddies.put(buddy.getId(), buddy);
            }
        }
        return cs;
    }

    public Buddy getBuddy(long uin) {
        return buddies.get(uin);
    }

    /**
     * 该方法应该在所有监听初始化后调用
     *
     * @throws ExecutionException
     * @throws InterruptedException
     * @throws IOException
     */
    public void getOnlineBuddies() {
        // clientId, psessionId, ui
        Future<Response> send = HttpRequestor.getInstance().send(
                new Request(U_GET_ONLINE_BUDDIES, Request.HttpMethod.GET)
                        .addHeader("Referer", H_REFERER)
                        .addParameter("clientid", token.getClientId())
                        .addParameter("psessionid", token.getPsessionId())
                        .addHeader("Origin", "http://s.web2.qq.com")
                        .addHeader("Host", "s.web2.qq.com")
                        .addParameter("ui", new Date().getTime()),
                new RequestCallbackAdapter() {
                    @Override
                    public void onSuccess(Response resp) throws IOException {
                        String reply = resp.getContent().asString();
                        BuddyStatus[] changes = onlineStatusResolver.resolveReply(reply);
                        for (BuddyStatus change : changes) {
                            EventBusHolder.getEventBus().post(change);
                        }
                    }
                }
        );
    }

    public List<Discussion> getDiscus() throws ExecutionException, InterruptedException {
        Response response = HttpRequestor.getInstance().send(new Request(U_GET_DISCUS, Request.HttpMethod.GET)
                        .addParameter("clientid", token.getClientId())
                        .addParameter("psessionid", token.getPsessionId())
                        .addParameter("vfwebqq", token.getVfwebqq())
                        .addParameter("t", new Date().getTime())
                        .addHeader("Origin", "http://s.web2.qq.com")
                        .addHeader("Host", "s.web2.qq.com")
                        .addHeader("REFERER", H_REFERER),
                null
        ).get();
        String s = response.getContent().asString();
        List<Discussion> discus = discuListResolver.resolveReply(s);
        for (Discussion discu : discus) {
            long did = discu.getId();
            discuGroups.put(did, discu);
            getDiscuInfo(did);
        }
//        return discus; // error
        return new LinkedList<Discussion>(discuGroups.values());
    }

    protected void getDiscuInfo(long did) throws ExecutionException, InterruptedException {
        Response resp = HttpRequestor.getInstance().send(new Request(U_GET_DISCU_INFO, Request.HttpMethod.GET)
                        .addParameter("did", did)
                        .addParameter("clientid", token.getClientId())
                        .addParameter("vfwebqq", token.getVfwebqq())
                        .addParameter("psessionid", token.getPsessionId())
                        .addParameter("t", "" + new Date().getTime())
                        .addHeader("Origin", "http://s.web2.qq.com")
                        .addHeader("Host", "s.web2.qq.com")
                        .addHeader("REFERER", H_REFERER),
                null
        ).get();
        String discus_info = resp.getContent().asString();
        Discussion discu = discuInfoResolver.resolveReply(discus_info);
        discuGroups.put(discu.getId(), discu);
    }

    public List<Group> getGroups() throws ExecutionException, InterruptedException {
        logger.info("获取群信息...");
        Response response = HttpRequestor.getInstance().send(new Request(U_GET_GROUPS, Request.HttpMethod.POST)
                        .addParameter("vfwebqq", token.getVfwebqq())
                        .addHeader("Origin", "http://s.web2.qq.com")
                        .addHeader("Host", "s.web2.qq.com")
                        .addHeader("REFERER", H_REFERER),
                null
        ).get();
        String s = response.getContent().asString();
        List<Group> groupList = groupListResolver.resolveReply(s);
        for (Group group : groupList) {
            long gid = group.getGid();
            groups.put(gid, group);
            getGroupInfo(group.getCode());
        }
        return new LinkedList<Group>(groups.values());
    }

    protected void getGroupInfo(long groupCode) throws ExecutionException, InterruptedException {
        Response resp = HttpRequestor.getInstance().send(new Request(U_GET_GROUP_INFO, Request.HttpMethod.GET)
                        .addParameter("gcode", groupCode)
                        .addParameter("vfwebqq", token.getVfwebqq())
                        .addHeader("Origin", "http://s.web2.qq.com")
                        .addHeader("Host", "s.web2.qq.com")
                        .addHeader("REFERER", H_REFERER),
                null
        ).get();
        String groupInfo = resp.getContent().asString();
        Group group = groupInfoResolver.resolveReply(groupInfo);
        groups.put(group.getGid(), group);
    }


    /**
     * 获取显示的账户
     * 获取好友qq帐号
     * 1 好友, 4 群
     *
     * @param uin
     * @param type
     * @return
     * @throws java.util.concurrent.ExecutionException
     * @throws InterruptedException
     * @throws java.io.IOException
     */
    public String getAccount(long uin, int type) throws ExecutionException, InterruptedException, IOException {
        Future<Response> send = HttpRequestor.getInstance().send(
                new Request(GET_ACCOUNT, Request.HttpMethod.GET)
                        .addHeader("Referer", H_REFERER)
                        .addHeader("Origin", "http://s.web2.qq.com")
                        .addHeader("Host", "s.web2.qq.com")
                        .addParameter("tuin", uin + "")
                        .addParameter("verifysession", "")
                        .addParameter("type", type + "")
                        .addParameter("code", "")
                        .addParameter("vfwebqq", token.getVfwebqq())
                        .addParameter("ui", new Date().getTime()),
                null
        );
        String s = send.get().getContent().asString();
        return Regexs.grep(s, "\"account\":([0-9]+)")[0][1];
    }

    // 个性签名
    public void getSummaryAsync(long uin) {
        HttpRequestor.getInstance().send(
                new Request(U_GET_ADDITIONAL, Request.HttpMethod.GET)
                        .addHeader("Referer", H_REFERER)
                        .addHeader("Origin", "http://s.web2.qq.com")
                        .addHeader("Host", "s.web2.qq.com")
                        .addParameter("tuin", uin)
                        .addParameter("vfwebqq", token.getVfwebqq())
                        .addParameter("ui", Math.random()),
                new RequestCallbackAdapter() {
                    @Override
                    public void onSuccess(Response resp) throws IOException {
                        String reply = resp.getContent().asString();
                        Summary summary = summaryResolver.resolveReply(reply);
                        if (summary == null) {
                            return;
                        }

                        long uin = summary.getUin();
                        Buddy buddy = buddies.get(uin);
                        if (buddy != null) {
                            buddy.setSummary(summary.getLnick());
                        }
                    }
                }
        );
    }

    /**
     * 该方法用于异步加载头像(加载头像比较耗时)
     * 当指定使用Cache时, 会先从Cache中加载, 如果Cache不存在,则从网络中下载
     * 之后会派发 {@link org.vacoor.xqq.core.event.Avatar}
     *
     * @param uin
     * @param type
     * @param cache
     */
    public void loadAvatar(final long uin, final int type, final boolean cache) {
        throw new UnsupportedOperationException("该方法暂时未完善");
    }

    public void getAvatarAsync(final long uin, final int type) {
        // 负载服务器任选一个
        final String AVATAR_URL = U_GET_AVATAR[(int) (uin % U_GET_AVATAR.length)];
        HttpRequestor.getInstance().send(
                new Request(AVATAR_URL, Request.HttpMethod.GET)
                        .addHeader("Referer", H_REFERER)
                        .addHeader("Origin", "http://s.web2.qq.com")
                        .addHeader("Host", "s.web2.qq.com")
                        .addParameter("type", type)     //1, 4
                        .addParameter("uin", uin)
                        .addParameter("vfwebqq", token.getVfwebqq()),
                new RequestCallbackAdapter() {
                    @Override
                    public void onSuccess(Response resp) throws IOException {
                        byte[] bytes = resp.getContent().asBytes();
                        EventBusHolder.getEventBus().post(new Avatar(uin, type, bytes));
                    }

                    @Override
                    public void onFailure(Response resp) {
                        logger.error("fail: {}", resp);
                        super.onFailure(resp);    //To change body of overridden methods use File | Settings | File Templates.
                    }

                    @Override
                    public void onException(Exception ex) {
                        logger.error("exception: {}", ex);
                        super.onException(ex);    //To change body of overridden methods use File | Settings | File Templates.
                    }
                }
        );
    }

    /**
     * 延迟加载头像和签名 Buddy
     */
    class LazyBuddy extends Buddy {

        public LazyBuddy(long id) {
            super(id);
        }

        public LazyBuddy(long id, String nick, String mark, String additional, Icon avatar) {
            super(id, nick, mark, additional, avatar);
        }

        @Override
        public Buddy setStatus(Status status) {
            Status old = getStatus();
            super.setStatus(status);

            boolean avatarLoaded = getAvatar() != null && getAvatar() != DEFAULT_AVATAR;
            boolean additLoaded = getSummary() != null;

            // 之前是可见状态, 并且头像和个人签名都加载完成
            if (!Status.isInvisible(old) && avatarLoaded && additLoaded) {
                return this;
            }
            if (!avatarLoaded) {
                logger.debug("load avatar for {}", getUin());
                getAvatarAsync(getUin(), 1);
            }
            if (!additLoaded) {
                logger.debug("load additional for {}", getUin());
                getSummaryAsync(getUin());
            }
            return this;
        }
    }
}

